参考地址:https://www.jianshu.com/p/19059060036b
session和token的区别:
- session是空间换时间,而token是时间换空间。session占用空间,但是可以管理过期时间,token管理部了过期时间,但是不占用空间.
- sessionId失效问题和token内包含。
- session基于cookie,app请求并没有cookie 。
- token更加安全(每次请求都需要带上)
Oauth2 密码授权流程
在oauth2协议里,每一个应用都有自己的一个clientId和clientSecret(需要去认证方申请),所以一旦想通过认证,必须要有认证方下发的clientId和secret。
1. pom
<!--security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
2. UserDetail实现认证第一步
MyUserDetailsService.java
@Autowired
private PasswordEncoder passwordEncoder;
/**
* 根据进行登录
* @param username
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
log.info("登录用户名:"+username);
String password = passwordEncoder.encode("123456");
//User三个参数 (用户名+密码+权限)
//根据查找到的用户信息判断用户是否被冻结
log.info("数据库密码:"+password);
return new User(username,password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
}
3. 获取token的控制器
1 @RestController
2 public class OauthController {
3
4 @Autowired
5 private ClientDetailsService clientDetailsService;
6 @Autowired
7 private AuthorizationServerTokenServices authorizationServerTokenServices;
8 @Autowired
9 private AuthenticationManager authenticationManager;
10
11 @PostMapping("/oauth/getToken")
12 public Object getToken(@RequestParam String username, @RequestParam String password, HttpServletRequest request) throws IOException {
13 Map<String,Object>map = new HashMap<>(8);
14 //进行验证
15 String header = request.getHeader("Authorization");
16 if (header == null && !header.startsWith("Basic")) {
17 map.put("code",500);
18 map.put("message","请求投中无client信息");
19 return map;
20 }
21 String[] tokens = this.extractAndDecodeHeader(header, request);
22 assert tokens.length == 2;
23 //获取clientId 和 clientSecret
24 String clientId = tokens[0];
25 String clientSecret = tokens[1];
26 //获取 ClientDetails
27 ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
28 if (clientDetails == null){
29 map.put("code",500);
30 map.put("message","clientId 不存在"+clientId);
31 return map;
32 //判断 方言 是否一致
33 }else if (!StringUtils.equals(clientDetails.getClientSecret(),clientSecret)){
34 map.put("code",500);
35 map.put("message","clientSecret 不匹配"+clientId);
36 return map;
37 }
38 //使用username、密码进行登录
39 UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username, password);
40 //调用指定的UserDetailsService,进行用户名密码验证
41 Authentication authenticate = authenticationManager.authenticate(authentication);
42 HrUtils.setCurrentUser(authenticate);
43 //放到session中
44 //密码授权 模式, 组建 authentication
45 TokenRequest tokenRequest = new TokenRequest(new HashMap<>(),clientId,clientDetails.getScope(),"password");
46
47 OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);
48 OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request,authentication);
49
50 OAuth2AccessToken token = authorizationServerTokenServices.createAccessToken(oAuth2Authentication);
51 map.put("code",200);
52 map.put("token",token.getValue());
53 map.put("refreshToken",token.getRefreshToken());
54 return map;
55 }
56
57 /**
58 * 解码请求头
59 */
60 private String[] extractAndDecodeHeader(String header, HttpServletRequest request) throws IOException {
61 byte[] base64Token = header.substring(6).getBytes("UTF-8");
62
63 byte[] decoded;
64 try {
65 decoded = Base64.decode(base64Token);
66 } catch (IllegalArgumentException var7) {
67 throw new BadCredentialsException("Failed to decode basic authentication token");
68 }
69
70 String token = new String(decoded, "UTF-8");
71 int delim = token.indexOf(":");
72 if (delim == -1) {
73 throw new BadCredentialsException("Invalid basic authentication token");
74 } else {
75 return new String[]{token.substring(0, delim), token.substring(delim + 1)};
76 }
77 }
78 }
4. 核心配置
(1)、Security 配置类 说明登录方式、登录页面、哪个url需要认证、注入登录失败/成功过滤器
1 @Configuration
2 public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
3
4 /**
5 * 注入 自定义的 登录成功处理类
6 */
7 @Autowired
8 private MyAuthenticationSuccessHandler mySuccessHandler;
9 /**
10 * 注入 自定义的 登录失败处理类
11 */
12 @Autowired
13 private MyAuthenticationFailHandler myFailHandler;
14
15 @Autowired
16 private ValidateCodeFilter validateCodeFilter;
17
18 /**
19 * 重写PasswordEncoder 接口中的方法,实例化加密策略
20 * @return 返回 BCrypt 加密策略
21 */
22 @Bean
23 public PasswordEncoder passwordEncoder(){
24 return new BCryptPasswordEncoder();
25 }
26
27 @Override
28 protected void configure(HttpSecurity http) throws Exception {
29 //在UsernamePasswordAuthenticationFilter 过滤器前 加一个过滤器 来搞验证码
30 http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
31 //表单登录 方式
32 .formLogin()
33 .loginPage("/authentication/require")
34 //登录需要经过的url请求
35 .loginProcessingUrl("/authentication/form")
36 .passwordParameter("pwd")
37 .usernameParameter("user")
38 .successHandler(mySuccessHandler)
39 .failureHandler(myFailHandler)
40 .and()
41 //请求授权
42 .authorizeRequests()
43 //不需要权限认证的url
44 .antMatchers("/oauth/*","/authentication/*","/code/image").permitAll()
45 //任何请求
46 .anyRequest()
47 //需要身份认证
48 .authenticated()
49 .and()
50 //关闭跨站请求防护
51 .csrf().disable();
52 //默认注销地址:/logout
53 http.logout().
54 //注销之后 跳转的页面
55 logoutSuccessUrl("/authentication/require");
56 }
57
58 /**
59 * 认证管理
60 *
61 * @return 认证管理对象
62 * @throws Exception 认证异常信息
63 */
64 @Override
65 @Bean
66 public AuthenticationManager authenticationManagerBean() throws Exception {
67 return super.authenticationManagerBean();
68 }
69 }
(2)、认证服务器
1 @Configuration
2 @EnableAuthorizationServer
3 public class MyAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
4 @Autowired
5 private AuthenticationManager authenticationManager;
6
7 @Autowired
8 private MyUserDetailsService userDetailsService;
9
10
11
12
13 @Override
14 public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
15 super.configure(security);
16 }
17
18 /**
19 * 客户端配置(给谁发令牌)
20 * @param clients
21 * @throws Exception
22 */
23 @Override
24 public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
25 clients.inMemory().withClient("internet_plus")
26 .secret("internet_plus")
27 //有效时间 2小时
28 .accessTokenValiditySeconds(72000)
29 //密码授权模式和刷新令牌
30 .authorizedGrantTypes("refresh_token","password")
31 .scopes( "all");
32 }
33
34 @Override
35 public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
36 endpoints
37 .authenticationManager(authenticationManager)
38 .userDetailsService(userDetailsService);
39 }
40 }
@EnableResourceServer
这个注解就决定了这是个资源服务器。它决定了哪些资源需要什么样的权限。
5、测试
来源:oschina
链接:https://my.oschina.net/u/4305979/blog/4334922