认证和授权
认证:登录,验证用户名和密码
授权:认证通过,给用户加上某些权限,这些权限控制用户是否可以访问资源服务器上哪些资源的资源
Spring Security
我们自己实现用户登录过程中,spring MVC,需要自己写拦截器,验证用户是否登录,以及登录接口相关逻辑,当用户登录成功后,把用户信息放到session中等等。这些操作spring Security帮我们都完成了。sprint security提供了用户名和密码登录,退出,会话管理等认证功能,我们需要简单的配置就行。
spring security给我们提供了要给简单的前台登录页面(用于测试),我们可以写自己的前台页面,然后post请求spring security的后台
OAuth2
是一种协议:允许博客(第三方应用)访问微信上存放的我的个人信息,然后用这个信息来登录博客。
客户端:浏览器,android,ios等
资源拥有者:通常是用户,可以是应用程序,即资源的拥有者(比如我们自己)。资源拥有者需要通过客户端访问资源。
授权服务器:(微信),我们自己搭建的授权服务器
资源服务器:我们自己的服务器
用户通过客户端访问微信,并且携带client_id(斗地主),让微信给斗地主授权,android拿到授权码,就去微信获取令牌,获取到令牌。就可以通过令牌访问斗地主了。
用户通过客户端访问自己系统,并且携带client_id(自己的),让自己的授权服务给微博授权,客户端(浏览器)拿到授权码,就去自己授权服务获取令牌,获取到令牌。就可以通过令牌访问自己的资源服务器。
授权服务和资源服务一般分布式系统是分开的,如果是单体应用,可是可以放到一起的。
Spring MVC+Spring Security
Spring Boot+Spring Security+jsp
使用jsp,一定要在spring Boot中配置webapp/WEB-INF
然后设置prefix和suffix
spring security逻辑:登录页面post:localhost:port/login,调用loadUserByUsername(),等返回的对象保存到SecurityContextHolder中。结束。
Spring Boot+SpringCloud+Spring Security+jsp
略
分析解释
配置:public void configure(ClientDetailsServiceConfigurer clients)
//类TokenEndpoint @RequestMapping(value = "/oauth/token", method=RequestMethod.POST) public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException { //会去找ClientDetail对象 String clientId = getClientId(principal); ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId); }
配置:authorizedGrantTypes
clients.inMemory() //client模式 .withClient("client_1") .authorizedGrantTypes("client_credentials", "refresh_token")//配置client_1有那些模式
当访问/oauth/token,会携带一个grant_type,spring security会去我们配置的模式["client_credentials", "refresh_token"]中找,看是否包含test,如果没有就报错,所以我们请求的grant_type必须是我们配置的type中的某一个。
授权码模式
说明:code
code会保存到authorizationCodeStore中【ConcurrentHashMap】
public class InMemoryAuthorizationCodeServices extends RandomValueAuthorizationCodeServices { protected final ConcurrentHashMap<String, OAuth2Authentication> authorizationCodeStore = new ConcurrentHashMap<String, OAuth2Authentication>(); @Override protected void store(String code, OAuth2Authentication authentication) { this.authorizationCodeStore.put(code, authentication); } @Override public OAuth2Authentication remove(String code) { OAuth2Authentication auth = this.authorizationCodeStore.remove(code); return auth; } }
当调用验证授权码的时候,调用HashMap的remove方法。所以授权码只允许使用一次
//类RandomValueAuthorizationCodeServices public OAuth2Authentication consumeAuthorizationCode(String code) throws InvalidGrantException { OAuth2Authentication auth = this.remove(code); if (auth == null) { throw new InvalidGrantException("Invalid authorization code: " + code); } return auth; }
请求/oauth/token,当指定了grant_type="authorization_code",必须传入一个code
Map<String, String> parameters = tokenRequest.getRequestParameters(); String authorizationCode = parameters.get("code"); String redirectUri = parameters.get(OAuth2Utils.REDIRECT_URI); if (authorizationCode == null) { throw new InvalidRequestException("An authorization code must be supplied."); }
SecurityContextHolder
保存用户信息
SecurityContextHolder.getContext().getAuthentication();
PreAuthorize
如果需要使用这个注解,需要在任意的@Configuration上添加@EnableGlobalMethodSecurity注解
@PostAuthorize:调用方法之后拦截
@Configuration @EnableResourceServer @EnableGlobalMethodSecurity(prePostEnabled = true)//激活方法上的PreAuthorize注解,如果需要使用@securedEnabled注解,在加上 securedEnabled= true public class ResourceServerConfig extends ResourceServerConfigurerAdapter {}
注解到@Controller方法上
@PreAuthorize("hasAuthority('course_pic_list')") @PreAuthorize("hasAnyAuthority('p1','p2')")
ClientDetailsServiceConfigurer
//spring-security-oauth2需要配置redirectUris //spring-cloud-starter-oauth2 不需要配置redirectUris clients.inMemory() .withClient("client-a") //client端唯一标识 .secret(passwordEncoder.encode("client-a")) //客户端的密码,这里的密码应该是加密后的 .authorizedGrantTypes("authorization_code") //授权模式标识 .scopes("read_user_info") //作用域 .resourceIds("resource1")//资源id .redirectUris("http://localhost:8080/callback"); //回调地址 // @formatter: on