參考文獻:html
Spring Security Architecture
前端
What is authentication in Spring Security?
java
Spring Security是一個可以爲基於Spring的企業應用系統提供聲明式的安全訪問控制解決方案的安全框架。它提供了一組能夠在Spring應用上下文中配置的Bean,爲應用系統提供聲明式的安全訪問控制功能,減小了爲企業系統安全控制編寫大量重複代碼的工做。node
基本使用:
添加依賴:
web
-
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-security</artifactId>
- </dependency>
<!-- 安全框架 Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
這裏有一篇博客入門學習很不錯:Spring boot 中 Spring Security 使用改造5部曲spring
個人項目中的使用:
自定義的User對象:數據庫
-
-
-
-
- public class AnyUser extends User {
-
-
- private Long id;
-
- private String nickname;
-
- AnyUser(
- String username,
- String password,
- Collection<? extends GrantedAuthority> authorities
- ) {
- super(username, password, authorities);
- }
-
- public Long getId() {
- return id;
- }
-
- public void setId(Long id) {
- this.id = id;
- }
-
- public String getNickname() {
- return nickname;
- }
-
- public void setNickname(String nickname) {
- this.nickname = nickname;
- }
- }
/**
* 自定義的 User 對象
* 此 User 類不是咱們的數據庫裏的用戶類,是用來安全服務的
*/
public class AnyUser extends User {
//import org.springframework.security.core.userdetails.User;
private Long id;
private String nickname;
AnyUser(
String username,
String password,
Collection<? extends GrantedAuthority> authorities
) {
super(username, password, authorities);
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
}
繼承UserDetailsService:
首先這裏咱們須要重寫UserDetailsService接口,而後實現該接口中的loadUserByUsername方法,經過該方法查詢到對應的用戶,這裏之因此要實現UserDetailsService接口,是由於在Spring Security中咱們配置相關參數須要UserDetailsService類型的數據。
Spring Security 支持把權限劃分層次,高層次包含低層次的權限,好比
`ROLE_AMDIN,ROLE_USER`兩個權限,若用戶擁有了ROLE_AMDIN權限,那麼至關於有了ROLE_USER權限。用戶被受權了ADMIN,那麼就至關於有其餘全部的權限。
-
-
-
- @Service
- class AnyUserDetailsService implements UserDetailsService {
-
- private final UserService userService;
-
- public AnyUserDetailsService(UserService userService){
- this.userService = userService;
- }
-
- @Override
- public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
- com.zhou.model.User user = userService.getByEmail(s);
- if (user == null){
- throw new UsernameNotFoundException("用戶不存在");
- }
- List<SimpleGrantedAuthority> authorities = new ArrayList<>();
-
- authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
- AnyUser anyUser = new AnyUser(s, user.getPassword(), authorities);
- anyUser.setId(user.getId());
- anyUser.setNickname(user.getNickname());
- return anyUser;
- }
-
- }
/**
* 自定義 UserDetailsService
*/
@Service
class AnyUserDetailsService implements UserDetailsService {
private final UserService userService;
public AnyUserDetailsService(UserService userService){
this.userService = userService;
}
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
com.zhou.model.User user = userService.getByEmail(s);
if (user == null){
throw new UsernameNotFoundException("用戶不存在");
}
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
//對應的權限添加
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
AnyUser anyUser = new AnyUser(s, user.getPassword(), authorities);
anyUser.setId(user.getId());
anyUser.setNickname(user.getNickname());
return anyUser;
}
}
安全控制中心:
-
-
-
- @EnableWebSecurity
- public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
-
- private final UserDetailsService userDetailsService;
-
- public WebSecurityConfig(AnyUserDetailsService userDetailsService){
- this.userDetailsService = userDetailsService;
- }
-
- @Override
- protected void configure(AuthenticationManagerBuilder auth) throws Exception {
- auth.userDetailsService(this.userDetailsService);
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- http
- .authorizeRequests()
- .antMatchers("/user/**","/news/**").authenticated()
- .anyRequest().permitAll()
- .and()
- .formLogin()
- .loginPage("/login")
- .defaultSuccessUrl("/user", true)
- .permitAll()
- .and()
- .logout()
- .permitAll()
- .and().csrf().disable();
- }
-
- }
/**
* 安全控制中心
*/
@EnableWebSecurity//@EnableWebMvcSecurity 註解開啓Spring Security的功能
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final UserDetailsService userDetailsService;
public WebSecurityConfig(AnyUserDetailsService userDetailsService){
this.userDetailsService = userDetailsService;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(this.userDetailsService);
}
/**
* http.authorizeRequests()
.anyRequest().authenticated()
.and().formLogin().loginPage("/login")
//設置默認登陸成功跳轉頁面
.defaultSuccessUrl("/index").failureUrl("/login?error").permitAll()
.and()
//開啓cookie保存用戶數據
.rememberMe()
//設置cookie有效期
.tokenValiditySeconds(60 * 60 * 24 * 7)
//設置cookie的私鑰
.key("")
.and()
.logout()
//默認註銷行爲爲logout,能夠經過下面的方式來修改
.logoutUrl("/custom-logout")
//設置註銷成功後跳轉頁面,默認是跳轉到登陸頁面
.logoutSuccessUrl("")
.permitAll();
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()//authorizeRequests() 定義哪些URL須要被保護、哪些不須要被保護
.antMatchers("/user/**","/news/**").authenticated()
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/user", true)
.permitAll()
.and()
.logout()
.permitAll()
.and().csrf().disable();
}
}
Spring Security提供了一個過濾器來攔截請求並驗證用戶身份。若是用戶身份認證失敗,頁面就重定向到/login?error,而且頁面中會展示相應的錯誤信息。若用戶想要註銷登陸,能夠經過訪問@{/logout}請求,在完成註銷以後,頁面展示相應的成功消息。json
自定義登陸成功處理邏輯:
後端
使登錄成功後跳到登陸前頁面:安全
-
- @Component("myAuthenticationSuccessHandler")
- public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
-
- @Autowired
- private ObjectMapper objectMapper;
-
- @Override
- public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
- throws IOException, ServletException {
-
- super.onAuthenticationSuccess(request, response, authentication);
-
- String url=request.getRequestURI();
-
-
- new DefaultRedirectStrategy().sendRedirect(request, response, url);
-
- }
- }
//處理登陸成功的。
@Component("myAuthenticationSuccessHandler")
public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
@Autowired
private ObjectMapper objectMapper;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
//什麼都不作的話,那就直接調用父類的方法
super.onAuthenticationSuccess(request, response, authentication);
String url=request.getRequestURI();
//若是是要跳轉到某個頁面的
new DefaultRedirectStrategy().sendRedirect(request, response, url);
}
}
從新配置安全中心(代碼完成以後,修改配置config類代碼。添加2個註解,自動注入):
- @Autowired
- private AuthenticationSuccessHandler myAuthenticationSuccessHandler;
@Autowired
private AuthenticationSuccessHandler myAuthenticationSuccessHandler;
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- http
- .authorizeRequests()
- .antMatchers("/user/**","/news/**","/blog/manage/**","/blog/create/**").authenticated()
- .anyRequest().permitAll()
- .and()
- .formLogin()
- .loginPage("/login")
- .successHandler(myAuthenticationSuccessHandler)
- .permitAll()
- .and()
- .logout()
- .permitAll()
- .and().csrf().disable();
- }
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()//authorizeRequests() 定義哪些URL須要被保護、哪些不須要被保護
.antMatchers("/user/**","/news/**","/blog/manage/**","/blog/create/**").authenticated()
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/login")
.successHandler(myAuthenticationSuccessHandler)//登錄成功處理
.permitAll()
.and()
.logout()
.permitAll()
.and().csrf().disable();
}
QQ登陸實現:
準備工做:
- 一、在 QQ互聯 申請成爲開發者,並建立應用,獲得APP ID 和 APP Key。
- 二、瞭解QQ登陸時的 網站應用接入流程。(必須看完看懂)。
爲了方便各位測試,這裏直接提供一個可使用的:
APP ID:101386962
APP Key:2a0f820407df400b84a854d054be8b6a
提醒:由於回調地址不是 http://localhost ,因此在啓動我提供的demo時,須要在host文件中添加一行:127.0.0.1 www.ictgu.cn
後端詳解:
一、自定義 QQAuthenticationFilter 繼承 AbstractAuthenticationProcessingFilter:
- import com.alibaba.fastjson.JSON;
- import org.jsoup.Jsoup;
- import org.jsoup.nodes.Document;
- import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
- import org.springframework.security.core.Authentication;
- import org.springframework.security.core.AuthenticationException;
- import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
- import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
-
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
-
- public class QQAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
- private final static String CODE = "code";
-
-
-
-
- private final static String accessTokenUri = "https://graph.qq.com/oauth2.0/token";
-
-
-
-
- private final static String grantType = "authorization_code";
-
-
-
-
- public static final String clientId = "101386962";
-
-
-
-
- private final static String clientSecret = "2a0f820407df400b84a854d054be8b6a";
-
-
-
-
- private final static String redirectUri = "http://www.ictgu.cn/login/qq";
-
-
-
-
- private final static String openIdUri = "https://graph.qq.com/oauth2.0/me?access_token=";
-
-
-
-
- private final static String TOKEN_ACCESS_API = "%s?grant_type=%s&client_id=%s&client_secret=%s&code=%s&redirect_uri=%s";
-
- public QQAuthenticationFilter(String defaultFilterProcessesUrl) {
- super(new AntPathRequestMatcher(defaultFilterProcessesUrl, "GET"));
- }
-
- @Override
- public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
- String code = request.getParameter(CODE);
- String tokenAccessApi = String.format(TOKEN_ACCESS_API, accessTokenUri, grantType, clientId, clientSecret, code, redirectUri);
- QQToken qqToken = this.getToken(tokenAccessApi);
- if (qqToken != null){
- String openId = getOpenId(qqToken.getAccessToken());
- if (openId != null){
-
- UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(qqToken.getAccessToken(), openId);
-
- return this.getAuthenticationManager().authenticate(authRequest);
- }
- }
- return null;
- }
-
- private QQToken getToken(String tokenAccessApi) throws IOException{
- Document document = Jsoup.connect(tokenAccessApi).get();
- String tokenResult = document.text();
- String[] results = tokenResult.split("&");
- if (results.length == 3){
- QQToken qqToken = new QQToken();
- String accessToken = results[0].replace("access_token=", "");
- int expiresIn = Integer.valueOf(results[1].replace("expires_in=", ""));
- String refreshToken = results[2].replace("refresh_token=", "");
- qqToken.setAccessToken(accessToken);
- qqToken.setExpiresIn(expiresIn);
- qqToken.setRefresh_token(refreshToken);
- return qqToken;
- }
- return null;
- }
-
- private String getOpenId(String accessToken) throws IOException{
- String url = openIdUri + accessToken;
- Document document = Jsoup.connect(url).get();
- String resultText = document.text();
- Matcher matcher = Pattern.compile("\"openid\":\"(.*?)\"").matcher(resultText);
- if (matcher.find()){
- return matcher.group(1);
- }
- return null;
- }
-
- class QQToken {
-
-
-
-
- private String accessToken;
-
-
-
-
- private int expiresIn;
-
-
-
-
- private String refresh_token;
-
- String getAccessToken() {
- return accessToken;
- }
-
- void setAccessToken(String accessToken) {
- this.accessToken = accessToken;
- }
-
- public int getExpiresIn() {
- return expiresIn;
- }
-
- void setExpiresIn(int expiresIn) {
- this.expiresIn = expiresIn;
- }
-
- public String getRefresh_token() {
- return refresh_token;
- }
-
- void setRefresh_token(String refresh_token) {
- this.refresh_token = refresh_token;
- }
- }
- }
import com.alibaba.fastjson.JSON;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class QQAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private final static String CODE = "code";
/**
* 獲取 Token 的 API
*/
private final static String accessTokenUri = "https://graph.qq.com/oauth2.0/token";
/**
* grant_type 由騰訊提供
*/
private final static String grantType = "authorization_code";
/**
* client_id 由騰訊提供
*/
public static final String clientId = "101386962";
/**
* client_secret 由騰訊提供
*/
private final static String clientSecret = "2a0f820407df400b84a854d054be8b6a";
/**
* redirect_uri 騰訊回調地址
*/
private final static String redirectUri = "http://www.ictgu.cn/login/qq";
/**
* 獲取 OpenID 的 API 地址
*/
private final static String openIdUri = "https://graph.qq.com/oauth2.0/me?access_token=";
/**
* 獲取 token 的地址拼接
*/
private final static String TOKEN_ACCESS_API = "%s?grant_type=%s&client_id=%s&client_secret=%s&code=%s&redirect_uri=%s";
public QQAuthenticationFilter(String defaultFilterProcessesUrl) {
super(new AntPathRequestMatcher(defaultFilterProcessesUrl, "GET"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
String code = request.getParameter(CODE);
String tokenAccessApi = String.format(TOKEN_ACCESS_API, accessTokenUri, grantType, clientId, clientSecret, code, redirectUri);
QQToken qqToken = this.getToken(tokenAccessApi);
if (qqToken != null){
String openId = getOpenId(qqToken.getAccessToken());
if (openId != null){
// 生成驗證 authenticationToken
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(qqToken.getAccessToken(), openId);
// 返回驗證結果
return this.getAuthenticationManager().authenticate(authRequest);
}
}
return null;
}
private QQToken getToken(String tokenAccessApi) throws IOException{
Document document = Jsoup.connect(tokenAccessApi).get();
String tokenResult = document.text();
String[] results = tokenResult.split("&");
if (results.length == 3){
QQToken qqToken = new QQToken();
String accessToken = results[0].replace("access_token=", "");
int expiresIn = Integer.valueOf(results[1].replace("expires_in=", ""));
String refreshToken = results[2].replace("refresh_token=", "");
qqToken.setAccessToken(accessToken);
qqToken.setExpiresIn(expiresIn);
qqToken.setRefresh_token(refreshToken);
return qqToken;
}
return null;
}
private String getOpenId(String accessToken) throws IOException{
String url = openIdUri + accessToken;
Document document = Jsoup.connect(url).get();
String resultText = document.text();
Matcher matcher = Pattern.compile("\"openid\":\"(.*?)\"").matcher(resultText);
if (matcher.find()){
return matcher.group(1);
}
return null;
}
class QQToken {
/**
* token
*/
private String accessToken;
/**
* 有效期
*/
private int expiresIn;
/**
* 刷新時用的 token
*/
private String refresh_token;
String getAccessToken() {
return accessToken;
}
void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public int getExpiresIn() {
return expiresIn;
}
void setExpiresIn(int expiresIn) {
this.expiresIn = expiresIn;
}
public String getRefresh_token() {
return refresh_token;
}
void setRefresh_token(String refresh_token) {
this.refresh_token = refresh_token;
}
}
}
說明:Filter 過濾時執行的方法是 doFilter(),因爲 QQAuthenticationFilter 繼承了 AbstractAuthenticationProcessingFilter,因此過濾時使用的是父類的doFilter() 方法。
說明:doFilter()方法中,有一步是 attemptAuthentication(request, response) 即爲 QQAuthenticationFilter 中實現的方法。這個方法中調用了 this.getAuthenticationManager().authenticate(authRequest),這裏自定義了類 QQAuthenticationManager,代碼以下:
- import com.alibaba.fastjson.JSON;
- import com.alibaba.fastjson.JSONObject;
- import com.zhou.model.User;
- import org.jsoup.Jsoup;
- import org.jsoup.nodes.Document;
- import org.springframework.security.authentication.AuthenticationManager;
- import org.springframework.security.authentication.BadCredentialsException;
- import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
- import org.springframework.security.core.Authentication;
- import org.springframework.security.core.AuthenticationException;
- import org.springframework.security.core.GrantedAuthority;
- import org.springframework.security.core.authority.SimpleGrantedAuthority;
- import java.io.IOException;
- import java.util.ArrayList;
- import java.util.List;
-
- import static com.zhou.config.qq.QQAuthenticationFilter.clientId;
-
- public class QQAuthenticationManager implements AuthenticationManager {
- private static final List<GrantedAuthority> AUTHORITIES = new ArrayList<>();
-
-
-
-
- private final static String userInfoUri = "https://graph.qq.com/user/get_user_info";
-
-
-
-
- private final static String USER_INFO_API = "%s?access_token=%s&oauth_consumer_key=%s&openid=%s";
-
- static {
- AUTHORITIES.add(new SimpleGrantedAuthority("ROLE_USER"));
- }
-
- @Override
- public Authentication authenticate(Authentication auth) throws AuthenticationException {
- if (auth.getName() != null && auth.getCredentials() != null) {
- User user = null;
- try {
- user = getUserInfo(auth.getName(), (String) (auth.getCredentials()));
- } catch (Exception e) {
- e.printStackTrace();
- }
- return new UsernamePasswordAuthenticationToken(user,
- null, AUTHORITIES);
- }
- throw new BadCredentialsException("Bad Credentials");
- }
-
- private User getUserInfo(String accessToken, String openId) throws Exception {
- String url = String.format(USER_INFO_API, userInfoUri, accessToken, clientId, openId);
- Document document;
- try {
- document = Jsoup.connect(url).get();
- } catch (IOException e) {
- throw new BadCredentialsException("Bad Credentials!");
- }
- String resultText = document.text();
- JSONObject json = JSON.parseObject(resultText);
-
- User user = new User();
- user.setNickname(json.getString("nickname"));
- user.setEmail("暫無。。。。");
-
-
-
- user.setAvatar(json.getString("figureurl_qq_2"));
-
- return user;
- }
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zhou.model.User;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static com.zhou.config.qq.QQAuthenticationFilter.clientId;
public class QQAuthenticationManager implements AuthenticationManager {
private static final List<GrantedAuthority> AUTHORITIES = new ArrayList<>();
/**
* 獲取 QQ 登陸信息的 API 地址
*/
private final static String userInfoUri = "https://graph.qq.com/user/get_user_info";
/**
* 獲取 QQ 用戶信息的地址拼接
*/
private final static String USER_INFO_API = "%s?access_token=%s&oauth_consumer_key=%s&openid=%s";
static {
AUTHORITIES.add(new SimpleGrantedAuthority("ROLE_USER"));
}
@Override
public Authentication authenticate(Authentication auth) throws AuthenticationException {
if (auth.getName() != null && auth.getCredentials() != null) {
User user = null;
try {
user = getUserInfo(auth.getName(), (String) (auth.getCredentials()));
} catch (Exception e) {
e.printStackTrace();
}
return new UsernamePasswordAuthenticationToken(user,
null, AUTHORITIES);
}
throw new BadCredentialsException("Bad Credentials");
}
private User getUserInfo(String accessToken, String openId) throws Exception {
String url = String.format(USER_INFO_API, userInfoUri, accessToken, clientId, openId);
Document document;
try {
document = Jsoup.connect(url).get();
} catch (IOException e) {
throw new BadCredentialsException("Bad Credentials!");
}
String resultText = document.text();
JSONObject json = JSON.parseObject(resultText);
User user = new User();
user.setNickname(json.getString("nickname"));
user.setEmail("暫無。。。。");
//user.setGender(json.getString("gender"));
//user.setProvince(json.getString("province"));
//user.setYear(json.getString("year"));
user.setAvatar(json.getString("figureurl_qq_2"));
return user;
}
說明:QQAuthenticationManager 的做用是經過傳來的 token 和 openID 去請求騰訊的getUserInfo接口,獲取騰訊用戶的信息,並生成新的 Authtication 對象。
接下來就是要將 QQAuthenticationFilter 與 QQAuthenticationManager 結合,配置到 Spring Security 的過濾器鏈中。代碼以下:
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- http
- .authorizeRequests()
- .antMatchers("/user/**","/news/**","/blog/manage/**").authenticated()
- .anyRequest().permitAll()
- .and()
- .formLogin()
- .loginPage("/login")
- .successHandler(myAuthenticationSuccessHandler)
- .permitAll()
- .and()
- .logout()
- .permitAll()
- .and().csrf().disable();
-
- http.addFilterAt(qqAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
- }
-
- /**
- * 自定義 QQ登陸 過濾器
- */
- private QQAuthenticationFilter qqAuthenticationFilter(){
- QQAuthenticationFilter authenticationFilter = new QQAuthenticationFilter("/login/qq");
-
-
-
- MyAuthenticationSuccessHandler successHandler = new MyAuthenticationSuccessHandler();
- authenticationFilter.setAuthenticationManager(new QQAuthenticationManager());
- authenticationFilter.setAuthenticationSuccessHandler(successHandler);
- return authenticationFilter;
- }
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()//authorizeRequests() 定義哪些URL須要被保護、哪些不須要被保護
.antMatchers("/user/**","/news/**","/blog/manage/**").authenticated()
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/login")
.successHandler(myAuthenticationSuccessHandler)//登錄成功處理
.permitAll()
.and()
.logout()
.permitAll()
.and().csrf().disable();
// 在 UsernamePasswordAuthenticationFilter 前添加 QQAuthenticationFilter
http.addFilterAt(qqAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
/**
* 自定義 QQ登陸 過濾器
*/
private QQAuthenticationFilter qqAuthenticationFilter(){
QQAuthenticationFilter authenticationFilter = new QQAuthenticationFilter("/login/qq");
//SimpleUrlAuthenticationSuccessHandler successHandler = new SimpleUrlAuthenticationSuccessHandler();
//successHandler.setAlwaysUseDefaultTargetUrl(true);
//successHandler.setDefaultTargetUrl("/user");
MyAuthenticationSuccessHandler successHandler = new MyAuthenticationSuccessHandler();
authenticationFilter.setAuthenticationManager(new QQAuthenticationManager());
authenticationFilter.setAuthenticationSuccessHandler(successHandler);
return authenticationFilter;
}
說明:因爲騰訊的回調地址是 /login/qq,因此 QQAuthenticationFilter 攔截的路徑是 /login/qq,而後將 QQAuthenticationFilter 置於 UsernamePasswordAuthenticationFilter 相同級別的位置。
前端說明:
前端很簡單,一個QQ登錄按鈕,代碼以下:
- <a href="https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=101386962&redirect_uri=http://www.ictgu.cn/login/qq&state=test" class="btn btn-primary btn-block">QQ登陸</a>
<a href="https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=101386962&redirect_uri=http://www.ictgu.cn/login/qq&state=test" class="btn btn-primary btn-block">QQ登陸</a>
其餘說明: 騰訊官網原話:openid是此網站上惟一對應用戶身份的標識,網站可將此ID進行存儲便於用戶下次登陸時辨識其身份,或將其與用戶在網站上的原有帳號進行綁定。 經過QQ登陸獲取的 openid 用於與本身網站的帳號一一對應。