Spring Security
江南一点雨牛逼
添加security依赖,不需要配置,默认为所有接口进行安全管理
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.java</groupId>
<artifactId>security</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>security</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.5.3</version>
</dependency>
</dependencies>
<!-- <build>-->
<!-- <plugins>-->
<!-- <plugin>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-maven-plugin</artifactId>-->
<!-- </plugin>-->
<!-- </plugins>-->
<!-- </build>-->
</project>
密码在项目启动时随机生成
用户名user
配置用户名和密码
server.port=8888
spring.security.user.name=java
spring.security.user.password=123
spring.security.user.roles=admin
或者使用配置文件
spring5开始之后,security中的密码必须加密
package org.java.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//实例表示用户不需要加密,可以直接登录,方案已经过期
@Bean
PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//基于内存认证
auth.inMemoryAuthentication()
.withUser("java").password("123").roles("user")
.and()
.withUser("admin").password("123").roles("admin");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//匹配用户
http.authorizeRequests()
// 匹配路径
.antMatchers("/admin/**").hasRole("admin")
//具备多个角色中的一个
.antMatchers("user/**").hasAnyRole("admin","user")
// 其他路径需要认证之后访问
.anyRequest().authenticated()
.and()
//表单登录
.formLogin()
//表单登录的路径,默认为login,postman测试最好配置一下
.loginProcessingUrl("/doLogin")
//和登录相关的接口可以直接访问
.permitAll()
.and()
//csrf攻击,测试时取消
.csrf().disable();
}
}
//具备多个角色中的一个
.antMatchers("user/**").access("hasAnyRole('user','admin')")
.loginProcessingUrl("/doLogin")
//登录页面,访问接口需要登录的话会跳转到登录页面上去,可以自己进行配置
.loginPage("/login")
登录表单的详细配置
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//实例表示用户不需要加密,可以直接登录,方案已经过期
@Bean
PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//基于内存认证
auth.inMemoryAuthentication()
.withUser("java").password("123").roles("user")
.and()
.withUser("admin").password("123").roles("admin");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//匹配用户
http.authorizeRequests()
// 匹配路径
.antMatchers("/admin/**").hasRole("admin")
//具备多个角色中的一个
.antMatchers("user/**").access("hasAnyRole('user','admin')")
// 其他路径需要认证之后访问
.anyRequest().authenticated()
.and()
//表单登录
.formLogin()
//表单登录的路径,默认为login,postman测试最好配置一下
.loginProcessingUrl("/doLogin")
//登录页面,访问接口需要登录的话会跳转到登录页面上去,可以自己进行配置
.loginPage("/login")
//配置用户名和密码参数
.usernameParameter("uname")
.passwordParameter("passwd")
//登录后服务端跳转接口.前后端不分离
// .successForwardUrl("")
//前后端分离,提供json数据,登录成功的处理
.successHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {
//json数据
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
Map<String,Object> map = new HashMap<>();
map.put("status",200);
//获取登录对象
map.put("msg",authentication.getPrincipal());
//输出为json字符串
out.write(new ObjectMapper().writeValueAsString(map));
out.flush();
out.close();
}
})
//登录失败的方式
.failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp, AuthenticationException e) throws IOException, ServletException {
//json数据
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
Map<String,Object> map = new HashMap<>();
map.put("status",401);
if(e instanceof LockedException){
map.put("msg","账户被锁定,登录失败");
}else if(e instanceof BadCredentialsException){
map.put("msg","用户名或密码输入错误,登录失败");
}else if(e instanceof DisabledException){
map.put("msg","账户被禁用,登录失败");
}else if(e instanceof AccountExpiredException){
map.put("msg","账户过期,登录失败");
}else if(e instanceof CredentialsExpiredException){
map.put("msg","密码过期,登录失败");
}else{
map.put("msg","登录失败");
}
//输出为json字符串
out.write(new ObjectMapper().writeValueAsString(map));
out.flush();
out.close();
}
})
//和登录相关的接口可以直接访问
.permitAll()
.and()
//csrf攻击,测试时取消
.csrf().disable();
}
}
ctrl+H查看继承关系
"authorities": [
{
"authority": "ROLE_admin"
}
],
authority会自动加上ROLE_前缀
注销登录
//和登录相关的接口可以直接访问
.permitAll()
.and()
.logout()
//注销登录
.logoutUrl("/logout")
.logoutSuccessHandler(new LogoutSuccessHandler() {
@Override
public void onLogoutSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {
//json数据
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
Map<String,Object> map = new HashMap<>();
map.put("status",200);
//获取登录对象
map.put("msg","注销登录成功");
//输出为json字符串
out.write(new ObjectMapper().writeValueAsString(map));
out.flush();
out.close();
}
})
.and()
//csrf攻击,测试时取消
.csrf().disable();
多个HttpSecurity
package org.java.config;
@Configuration
public class MultiHttpSecurityConfig {
@Bean
PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
// @Override
@Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//基于内存认证
auth.inMemoryAuthentication()
.withUser("java").password("123").roles("user")
.and()
.withUser("admin").password("123").roles("admin");
}
@Configuration
//设置优先级
@Order(1)
//先和这个函数匹配,如果匹配上面,再和下面的函数进行匹配
public static class AdminSecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/admin/**").authorizeRequests().anyRequest().hasAnyRole("admin");
}
}
@Configuration
public static class OtherSecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and()
.formLogin()
.loginProcessingUrl("/doLogin")
.permitAll()
.and()
.csrf().disable();
}
}
}
密码加密
可逆加密,对称加密,非对称加密
密码加盐:相同明文加密后生成的字段不相同,维护盐字段
@RunWith(SpringRunner.class)
@SpringBootTest
public class SecurityApplicationTests {
@Test
public void contextLoads(){
for (int i = 0; i < 10; i++) {
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
System.out.println(encoder.encode("123"));
}
}
}
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
// @Override
@Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//基于内存认证
auth.inMemoryAuthentication()
.withUser("java").password("$2a$10$uxLNq0J1Fm6cwlsAA3Z08u1Geh/ZLD85E18Wv8hC8SOjr4Jvp68K.").roles("user")
.and()
.withUser("admin").password("$2a$10$wBu9WqZVu7IrVd.9E1P1EuvpHfkEqUsTjpXs3UkwxUelMKFWq4ppi").roles("admin");
}
方法安全
加一个注解
@Configuration
//方法执行之前进行校验,securedEnabled可以写表达式
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
public class MultiHttpSecurityConfig {
@Service
public class MethodService {
@PreAuthorize("hasRole('admin')")
public String admin(){
return "hello admin";
}
@Secured("ROLE_user")
public String user(){
return "hello user";
}
@PreAuthorize("hasAnyRole('admin','user')")
public String hello(){
return "hello ha";
}
}
@Autowired
MethodService methodService;
@GetMapping("/hello1")
public String hello1(){
return methodService.admin();
}
@GetMapping("/hello2")
public String hello2(){
return methodService.user();
}
@GetMapping("/hello3")
public String hello3(){
return methodService.hello();
}
基于数据库的认证
导入表,user,role,user_role
添加数据库依赖
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
<scope>runtime</scope>
</dependency>
<!-- 数据库驱动-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
配置文件
server.port=8888
#spring.security.user.name=java
#spring.security.user.password=123
#spring.security.user.roles=admin
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:mysql://localhost:3307/javagirl?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
创建Role文件
package org.java.bean;
public class Role {
private Integer id;
private String usename;
private String password;
private Boolean enabled;
private Boolean locked;
@Override
public String toString() {
return "User{" +
"id=" + id +
", usename='" + usename + '\'' +
", password='" + password + '\'' +
", enabled=" + enabled +
", locked=" + locked +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsename() {
return usename;
}
public void setUsename(String usename) {
this.usename = usename;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Boolean getEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public Boolean getLocked() {
return locked;
}
public void setLocked(Boolean locked) {
this.locked = locked;
}
}
package org.java.bean;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
//当系统需要调用属性去验证用户是否登录成功,每个人定义的属性不一样,这个函数可以获取用户名,密码
public class User implements UserDetails {
private Integer id;
private String username;
private String password;
private Boolean enabled;
private Boolean locked;
private List<Role> roles;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Override
public String getUsername() {
return username;
}
//账户是否未过期
@Override
public boolean isAccountNonExpired() {
//没有过期
return true;
}
//账户是否未锁定
@Override
public boolean isAccountNonLocked() {
//账户未锁定
return !locked;
}
//凭证是否未过期
@Override
public boolean isCredentialsNonExpired() {
return true;
}
//账户是否可用
@Override
public boolean isEnabled() {
return enabled;
}
public void setUsername(String username) {
this.username = username;
}
//返回用户的所有角色
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
for (Role role : roles) {
authorities.add(new SimpleGrantedAuthority("ROLE_"+role.getUsername()));
}
return authorities;
}
@Override
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Boolean getEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
// 删掉,不然重复
// public Boolean getLocked() {
// return locked;
// }
public void setLocked(Boolean locked) {
this.locked = locked;
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
}
userService
@Service
public class UserService implements UserDetailsService {
@Autowired
UserMapper userMapper;
//根据用户名查询用户,参数为登录的用户名,返回用户对象
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userMapper.loadUserByUsername(username);
if(user == null){
throw new UsernameNotFoundException("用户不存在");
}
user.setRoles(userMapper.getUserRolesById(user.getId()));
return user;
}
}
userMapper接口
public interface UserMapper {
User loadUserByUsername(String username);
List<Role> getUserRolesById(Integer id);
}
userMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="org.java.mapper.UserMapper">
<select id="loadUserByUsername" resultType="org.java.bean.User">
select * from user where username=#{username};
</select>
<select id="getUserRolesById" resultType="org.java.bean.Role" parameterType="java.lang.Integer">
select * from role where id in (select rid from user_role where uid=#{id});
</select>
</mapper>
java: 不兼容的类型: org.springframework.security.core.userdetails.User无法转换为org.java.bean.User
从数据库加载用户,用户具备相应的角色
角色继承
'>'关系
放在security配置中,SecurityConfig
@Bean
RoleHierarchy roleHierarchy(){
RoleHierarchy roleHierarchy = new RoleHierarchyImpl();
String hierarchy = "ROLE_dba > ROLE_admin \n ROLE_admin > ROLE_user";
roleHierarchy.setHierachy(hierarchy);
return roleHierarchy;
}
动态配置权限
menu
package org.java.bean;
import java.util.List;
public class Menu {
private Integer id;
private String pattern;
private List<Role> roles;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getPattern() {
return pattern;
}
public void setPattern(String pattern) {
this.pattern = pattern;
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
}
menuService
@Service
public class MenuService {
@Autowired
MenuMapper menuMapper;
public List<Menu> getAllMenus(){
return menuMapper.getAllMenus();
}
}
menuMapper
public interface MenuMapper {
List<Menu> getAllMenus();
}
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="org.java.mapper.MenuMapper">
<resultMap id="BaseResultMap" type="org.java.bean.Menu">
<id property="pattern" column="id"/>
<result property="pattern" column="pattern"/>
<collection property="roles" ofType="org.java.bean.Role">
<id column="rid" property="id"/>
<result column="rname" property="name"/>
<result column="rnameZh" property="nameZh"></result>
</collection>
</resultMap>
<select id="getAllMenus" resultMap="BaseResultMap">
select m.*,r.`id` as rid,r.`name` as rname,r.`nameZh` as rnameZh from menu m left join menu_role mr on m.`id` =mr.`mid` left join role r on mr.`rid`=r.`id`;
</select>
</mapper>
Config下的MyFilter文件
package org.java.config;
import org.java.bean.Menu;
import org.java.bean.Role;
import org.java.service.MenuService;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import java.util.Collection;
import java.util.List;
//每次请求过滤,过滤器,分析出请求地址,确定地址需要什么角色
@Component
public class MyFilter implements FilterInvocationSecurityMetadataSource {
//路径匹配符,可以直接匹配规则和路径
AntPathMatcher pathMatcher = new AntPathMatcher();
@Override
public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
//请求的地址
String requestUrl =((FilterInvocation) o).getRequestUrl();
//所有的菜单,pattern里面定义的规则
List<Menu> allMenus = MenuService.getAllMenus();
//遍历menu
for (Menu menu : allMenus) {
//匹配menu和请求的地址
if(pathMatcher.match(menu.getPattern(),requestUrl)){
//需要的角色
List<Role> roles = menu.getRoles();
//生成需要的角色列表
String[] rolesStr = new String[roles.size()];
for (int i = 0; i < roles.size(); i++) {
rolesStr[i] = roles.get(i).getName();
}
return SecurityConfig.createList(rolesStr);
}
}
//额外的路径,等会儿做额外的处理
return SecurityConfig.createList("ROLE_login");
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
@Override
public boolean supports(Class<?> aClass) {
return false;
}
}
MyAccessDecisionManager文件
public class MyAccessDecisionManager implements AccessDecisionManager {
//第一个参数:登录成功后的参数 保存登录成功后用户信息,二哥参数:用来获取当前请求对象,第三个参数:MyFilter文件中的getAttribute的返回值(需要哪些角色)
@Override
public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {
for (ConfigAttribute attribute : collection) {
//需要的角色和ROLE_login匹配,登录后就能访问,查看是否登录
if("ROLE_login".equals(attribute.getAttribute())){
if(authentication instanceof AnonymousAuthenticationToken){
throw new AccessDeniedException("非法请求,未登录");
}else{
return;
}
}
//其他的判断
//需要的角色
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
//如果需要ABC三个角色中的一个
for (GrantedAuthority authority : authorities) {
if(authority.getAuthority().equals(attribute.getAttribute())){
return;
}
}
throw new AccessDeniedException("非法请求");
}
}
@Override
public boolean supports(ConfigAttribute configAttribute) {
return false;
}
@Override
public boolean supports(Class<?> aClass) {
return false;
}
}
SecurityConfig文件
@Autowired
MyFilter myFilter;
@Autowired
MyAccessDecisionManager myAccessDecisionManager;
@Override
protected void configure(HttpSecurity http) throws Exception{
http.authorizeRequests()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
@Override
public <O extends FilterSecurityInterceptor> O postProcess(O o) {
o.setAccessDecisionManager(MyAccessDecisionManager);
o.setSecurityMetadataSource(myFilter);
return o;
}
})
.and()
.formLogin()
.permitAll()
.and()
.csrf().disable;
}
OAuth2协议
开放的标准,允许用户用第三方应用访问该用户在某个网站上存放的私密资源。QQ登录,微信登录,支付宝登录
登录时不需要用户名和密码
通过令牌实现,一定时间内的权限
角色:
- 资源所有者,用户
- 客户端
- 授权服务器,验证用户提供的信息是否正确,返回令牌给第三方应用
- 资源服务器,提供给用户资源的服务器
配置授权服务器,AuthorizationServerConfig
package org.java.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
//有四种认证模式。下面支持password认证模式,前后端分离登录。第三方登录使用授权码模式
@Autowired
AuthenticationManager authenticationManager;
@Autowired
RedisConnectionFactory redisConnectionFactory;
@Autowired
UserDetailsService userDetailsService;
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//配置在内存里面
clients.inMemory()
//认证模式为password
.withClient("password")
//配置授权模式,两种
.authorizedGrantTypes("password","refresh_token")
//token过期时间1800ms
.accessTokenValiditySeconds(1800)
//资源id
.resourceIds("rid")
.scopes("all")
//密码,test文件中生成
.secret("$2a$10$uxLNq0J1Fm6cwlsAA3Z08u1Geh/ZLD85E18Wv8hC8SOjr4Jvp68K.");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//令牌的存储
endpoints.tokenStore(new RedisTokenStore(redisConnectionFactory))
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.allowFormAuthenticationForClients();
}
}
配置资源服务器ResourceServerConfig
package org.java.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
//配置config方法,指定资源id
resources.resourceId("rid").stateless(true);
}
@Override
public void configure(HttpSecurity http) throws Exception {
//资源服务器
http.authorizeRequests().antMatchers("/admin/**").hasRole("admin")
.antMatchers("/user/**").hasRole("user")
.anyRequest().authenticated();
}
}
先在授权服务器获取授权,再在资源服务器上查找资源
配置SecurityConfig
package org.java.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
@Override
protected UserDetailsService userDetailsService() {
return super.userDetailsService();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//基于内存认证
auth.inMemoryAuthentication()
.withUser("java").password("$2a$10$uxLNq0J1Fm6cwlsAA3Z08u1Geh/ZLD85E18Wv8hC8SOjr4Jvp68K.").roles("user")
.and()
.withUser("admin").password("$2a$10$uxLNq0J1Fm6cwlsAA3Z08u1Geh/ZLD85E18Wv8hC8SOjr4Jvp68K.").roles("admin");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/oauth/**")
.authorizeRequests()
.antMatchers("/oauth/**").permitAll()
.and().csrf().disable();
}
}
controller接口
package org.java.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/admin/hello")
public String admin(){
return "hello admin";
}
@GetMapping("/user/hello")
public String user(){
return "hello user";
}
@GetMapping("/hello")
public String hello(){
return "hello";
}
}
org.springframework.beans.factory.BeanDefinitionStoreException: Failed to process import candidates for configuration class [org.java.OauthApplication]; nested exception is java.io.FileNotFoundException: class path resource [org/springframework/boot/sql/init/dependency/DatabaseInitializationDependencyConfigurer.class] cannot be opened because it does not exist
access_token:需要获取其它资源
refresh_token:刷新资源的token
expire_in:过期时间
SpringBoot整合Shiro
不想敲了,就听听吧
添加shiro-web依赖,shiro-spring依赖
配置shiro,extends AuthorizingRealm
ShiroConfig
使用官方的starter进行shiro整合
Spring Security使用JSON登陆
默认的登陆数据是key/value形式
连按两下shift搜索
源码
获取用户名UsernamePasswordAuthenticationFilter
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
if (this.postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
String username = obtainUsername(request);
username = (username != null) ? username : "";
username = username.trim();
String password = obtainPassword(request);
password = (password != null) ? password : "";
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
protected String obtainUsername(HttpServletRequest request) {
return request.getParameter(this.usernameParameter);
}
配置MyAuthenticationFilter
package org.java.filter;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
public class MyAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (!request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
if(request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)){
//说明用户以JSON形式传递参数
//从request提取json数据
String username = null;
String password = null;
try {
Map<String,String> map = new ObjectMapper().readValue(request.getInputStream(), Map.class);
username = map.get("username");
password = map.get("password");
} catch (IOException e) {
e.printStackTrace();
}
username = (username != null) ? username : "";
username = username.trim();
password = (password != null) ? password : "";
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
return super.attemptAuthentication(request, response);
}
}
SecurityConfig
package org.java.Config;
import org.java.filter.MyAuthenticationFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and()
.formLogin().permitAll()
.and().csrf().disable();
http.addFilterAt(myAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
@Bean
MyAuthenticationFilter myAuthenticationFilter() throws Exception {
MyAuthenticationFilter filter = new MyAuthenticationFilter();
filter.setAuthenticationManager(authenticationManagerBean());
return filter;
}
}
package org.java.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(){
return "hello";
}
}
org.springframework.beans.factory.BeanDefinitionStoreException: Failed to process import candidates for configuration class [org.java.SecurityJsonApplication]; nested exception is java.io.FileNotFoundException: class path resource [org/springframework/boot/sql/init/dependency/DatabaseInitializationDependencyConfigurer.class] cannot be opened because it does not exist
SpringSecurity整合JWT
无状态登陆oauth2或者jwt
添加依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>