Spring Security
:在架构上将认证与授权分离,并提供了扩展点 Spring IoC
(控制反转),DI
(依赖注入)和 AOP
(面向切面编程)功能Spring
的应用程序提供身份验证和授权支持Spring MVC
有很好地集成 ,并配备了流行的安全算法实现捆绑在一起Filter
;对方法调用进行保护,最好于 AOP
Spring Security
在进行用户认证以及授予权限时通过各种各样的拦截器来控制权限的访问,从而实现安全Spring Security
对 Web
安全性的支持大量地依赖于 Servlet
过滤器 Spring Security
提供有若干个过滤器,能够拦截 Servlet
请求 Servlet
过滤器且令其正常工作必须在 web.xml
文件中使用
和
元素配置 FilterToBeanProxy
是特殊的Servlet过滤器 Spring
应用程序上下文中的一个 Bean
来完成 Bean
几乎和 Servlet
过滤器一样 javax.servlet.Filter
接口Spring
配置文件非 web.xml
文件中配置FilterToBeanProxy
代理给的 Bean
可以是 javax.servlet.Filter
的任意实现 Spring Security
的任何过滤器 Spring Security
要求至少配置四个而且可能一打或者更多的过滤器shiro
的功能都有 Spring Security
对 Oauth
、OpenID
也有支持 注:
添加相应依赖信息:包括 Thymeleaf 整合 security 依赖
org.springframework.boot spring-boot-starter-security
org.thymeleaf.extras thymeleaf-extras-springsecurity5
WebSecurityConfigurerAdapter
WebSecurityConfigurerAdapter
:自定义 Security 策略AuthenticationManagerBuilder
:自定义认证策略@EnableWebSecurity
:开启 WebSecurity 模式// AOP 实现
@EnableWebSecurity
public class SecurityConf extends WebSecurityConfigurerAdapter {// 请求授权@Override protected void configure(HttpSecurity http) throws Exception {// 链式编写:不同模块使用 and() 连接,也可以分开写// 对请求授权 http.authorizeRequests() // 不需要通过登录验证就可以被访问的资源路径.antMatchers("/", "/index", "/toLogin").permitAll()// 需要对应权限、角色访问.antMatchers("/level1/**").hasRole("vip1").antMatchers("/level2/**").hasRole("vip2").antMatchers("/level3/**").hasRole("vip3")// 上面的任何请求都必须经过身份验证.anyRequest().authenticated();// 表单登录,用户未登录时,访问任何资源都转跳到该路径,即登录页面http.formLogin()// 登录页面,设置为自定义的登录页面.loginPage("/toLogin")// 登录表单中 action 的地址,即实际处理认证请求的路径.loginProcessingUrl("/login")// 获取用户名输入框参数:默认 username.usernameParameter("username")// 获取密码输入框参数:默认 password.passwordParameter("password")// 登录认证成功后默认转跳的路径,登陆失败后返回原页面.successForwardUrl("/").failureForwardUrl("/toLogin").permitAll()// 注销http.logout()// 登出成功后返回的请求.logoutSuccessUrl("/").permitAll()// 关闭 csrf 功能:跨站请求伪造,默认只能通过 post 方式提交请求http.csrf().disable()// 记住我功能,使用 Cookie 缓存,默认有效期14天.rememberMe().rememberMeParameter("remember");}
}
?
:匹配任何单字符*
:匹配 0 或者任意数量的字符**
:匹配 0 或更多目录permitAll()
:允许任何访问denyAll()
:拒绝所有访问anonymous()
:允许匿名用户访问authenticated()
:允许认证的用户进行访问hasRole(String)
: hasAnyRole(String…)
: hasAuthority(String)
: hasAnyAuthority(String…)
: principal
: rememberMe()
: Remember-me
功能认证允许访问fullyAuthenticated()
: hasIpAddress(String)
: isAnonymous()
:当前委托人是否为匿名用户isRememberMe()
:当前主体是否是“记住我”的用户not()
:对其他访问结果求反AuthenticationManagerBuilder
:使用构造者方式来构建 inMemoryAuthentication()
passwordEncoder()
Spring security5
后,必须指定加密方式,不然程序会报错withUser()、password()、authorities()
and()
方法来连接下一个用户的添加@EnableWebSecurity
public class SecurityConf extends WebSecurityConfigurerAdapter {// 权限认证public void configure(AuthenticationManagerBuilder auth) throws Exception {// 在内存里面存储用户的身份认证和授权信息auth.inMemoryAuthentication() // 添加用户权限:用户名、密码(加密)、角色信息 .withUser("user").password(passwordEncoder().encode("123456")).roles("vip1")// 添加多个用户使用 and().and().withUser("admin").password(passwordEncoder().encode("123456")).roles("vip1", "vip2", "vip3")// 配置BCrypt加密.and().passwordEncoder(passwordEncoder()); }// 加密对象,密码需要加密才能安全保存@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}
}
jdbcAuthentication()
jdbc
的方式来查询用户和权限dataSource()
:指定数据库连接信息passwordEncoder()
:指定密码加密规则 usersByUsernameQuery()
、authoritiesByUsernameQuery()
Spring security
默认了查询用户、权限甚至还有群组用户授权的sql org......userdetails.jdbc.JdbcDaoImpl
中// 自动装配数据源对象
@Autowired
private DataSource dataSource;@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {// 使用 JDBC 存储身份认证信息auth.jdbcAuthentication()// 传入数据源.dataSource(dataSource)// 指定密码加密方式.passwordEncoder(passwordEncoder())// 根据用户名获取用户信息.usersByUsernameQuery("select username, password, status from Users where username = ?")// 根据用户名获取认证权限.authoritiesByUsernameQuery("select username, authority from Authority where username = ?");
}//定义加密对象
@Bean
private PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();
}
LDAP
:轻型目录访问协议,一个开放的,中立的,工业标准的应用协议 userSearchFilter()
、groupSearchFilter()
userSearchBase()
、groupSearchBase()
contextSource().url()
: LDAP
服务器的地址contextSource().root()
使用嵌入式 LDAP
服务器 @Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {LdapAuthenticationProviderConfigurer configurer = auth.ldapAuthentication().userSearchBase("ou=people").userSearchFilter("(uid={0})").groupSearchBase("ou=groups").groupSearchFilter("member={0}");configurer.passwordCompare().passwordEncoder(passwordEncoder()).passwordAttribute("passcode");configurer.contextSource().url("ldap://xxxxx.com:33389/dc=xxxxxx,dc=com");
}private PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();
}
自定义认证方式:不仅限于 MyBatis 使用
UserDetailsService
的 service
类 loadUserByUsername()
username
来匹配带有密码等信息的用户实体 User
类需要实现 UserDetails
Spring Security
所需要的信息Spring Security
的 User
类中 User
和 Spring Security
的 User
Spring Security
拿前台的数据比较,做出操作 @Service
public class UserService implements UserDetailsService {@Qualifier("userDao")@Autowiredprivate UserDao userDao;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {if (username == null){throw new UsernameNotFoundException("用户名不存在");}// 获取用户对象security.pojo.User user = userDao.getUserByName(username);List authorityList = new ArrayList<>();// 得到用户权限String role = user.getRoles();// 权限不为空放到到 List 集合中if (role != null && !"".equals(role)){authorityList.add(new SimpleGrantedAuthority(role.trim()));}// 封装到框架的 USer 类中并提交return new User(user.getUsername(),user.getPassword(),authorityList);}
}
Spring Security
自定义的 UserDetailsService
实现类 loadUserByUsername()
来查找用户@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate MyUserDetailsService userDetailsService;@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService)//密码加密方式和数据库密码加密方式要相同.passwordEncoder(passwordEncoder());}@Beanprivate PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}
}
整合 Thymeleaf 模板使用
命名空间
Apache Shiro
:一个强大且易用的 Java 安全框架 Subject
、 SecurityManager
和 Realms
Subject
:当前操作用户 Shiro
中,Subject
并不仅仅指人 Subject
代表当前用户的安全操作,SecurityManager
则管理所有用户的安全操作SecurityManager
:Shiro
框架的核心,典型的 Facade
模式 SecurityManager
来管理内部组件实例,并提供安全管理的各种服务Realm
:充当 Shiro
与应用安全数据间的 桥梁 或 连接器 Shiro
从应用配置的 Realm
中查找用户及其权限信息 Realm
实质上是安全相关的 DAO
Shiro
Shiro
时,必须至少指定一个 Realm
,用于认证和(或)授权 Realm
是可以的,但至少需要一个Shiro
内置了可连接大量安全数据源(又名目录)的 Realm
LDAP
、关系数据库(JDBC
)、类似 INI
的文本配置资源以及属性文件等Realm
不能满足需求,可以插入代表自定义数据源的 Realm
实现组件
UsernamePasswordToken
: Shiro
用来封装用户登录信息,使用用户的登录信息创建令牌 Token
Shiro
验证令牌是否具有合法身份以及相关权限AuthenticationInfo
:用户的角色信息集合,认证时使用AuthorizationInfo
,角色的权限信息集合,授权时使用DefaultWebSecurityManager
:安全管理器 Realm
需要注入到 DefaultWebSecurityManager
进行管理才能生效ShiroFilterFactoryBean
:过滤器工厂 Shiro
的基本运行机制是开发者定制规则,Shiro 去执行ShiroFilterFactoryBean
创建 Filter
对象来完成功能模块
Session Manager
:会话管理,用户登录后的session相关管理Cryptography
:加密,密码加密等Web Support
:Web支持,集成Web环境Caching
:缓存,用户信息、角色、权限等缓存到如redis等缓存中Concurrency
:多线程并发验证,在一个线程中开启另一个线程,可以把权限自动传播过去Testing
:测试支持Run As
:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问Remember Me
:记住我Java Security API
com.github.theborakompanioni thymeleaf-extras-shiro 2.1.0
org.apache.shiro shiro-spring-boot-web-starter 1.8.0
org.apache.logging.log4j log4j-to-slf4j
//仅有部分相关方法// 要登录请求处理:跳转到登录页面
@RequestMapping("/toLogin")
public String toLogin() {return "login";
}
// 登录处理
@RequestMapping("/login")
public String login(String username, String password, Model model) {// 获取当前用户 subject 对象Subject subject = SecurityUtils.getSubject();// 使用传进来的用户名、密码创建令牌UsernamePasswordToken token = new UsernamePasswordToken(username, password);try {// 执行登录操作:由框架进行处理// 认证、授权在 realm 中// 拦截规则在过滤器中定义subject.login(token);return "index";} catch (UnknownAccountException e) {// 拦截异常情况model.addAttribute("message", "用户名不存在");return "login";} catch (IncorrectCredentialsException e) {model.addAttribute("message", "密码错误");return "login";}}
// 认证异常请求处理
@RequestMapping("/unauthorized")
@ResponseBody
public String unauthorized(){return "未获得权限,无法访问";
}
public class UserRealm extends AuthorizingRealm {// 注入对象从数据库中查找用户信息@Autowiredprivate UserDao userDao;// 授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();// 得到认证时传入的用户对象(或用户名、id 等对象)User user = (User)principalCollection.getPrimaryPrincipal();// 或得到 Subject 当前用户对象// Subject subject = SecurityUtils.getSubject();// 转为自定义的 User 对象// User user = (User)subject.getPrincipal();// 硬授权:所有进入的用户都会授权 user:add// info.addStringPermission("user:add");// 获取用户角色信息并添加角色(或使用授权)info.addRole(user.getRoles());return info;}// 认证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {// 得到传进来的用户名String username = (String) authenticationToken.getPrincipal();// 通过用户名查询用户User user = userDao.getByName(username);// 如果用户为 nullif (user == null) {// 不正确时直接返回 null,会自动捕捉异常return null; }// 传入用户对象、密码、自定义 Realm 类名传进去由框架自动匹配return new SimpleAuthenticationInfo(user, user.getPassword(), getName());/* 第一个参数可以是用户对象、用户名、用户id 等可以在授权时获取此时传入的信息,密码会由框架自动匹配*/}
}
CustomRealm
和 SecurityManager
等注入到 spring
容器MyRealm
DefaultWebSecurityManager
MyRealm
注入到 DefaultWebSecurityManager
完成注册ShiroFilterFactoryBean
Filter
工厂实例@Configuration
public class ShiroCong {// Realm 对象,将自定义验证方式加入容器@Beanpublic UserRealm userRealm(){return new UserRealm();}// DefaultWebSecurityManager: 安全管理器// 权限管理,配置主要是 Realm 的管理认证@Beanpublic DefaultWebSecurityManager securityManager(){// 关联 Realm 对象return new DefaultWebSecurityManager(userRealm());}// ShiroFilterFactoryBean:过滤器对象// Filter 工厂,设置对应的过滤条件和跳转条件@Beanpublic ShiroFilterFactoryBean shiroFilterFactoryBean(){ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();// 关联 SecurityManager 对象bean.setSecurityManager(securityManager());// 使用 LinkHashMap 存放路径拦截规则,有序:先配置先生效Map filterMap = new LinkedHashMap<>();// 默认无法配置多角色访问同一权限,需要自定义修改 shiro 过滤器配置// 授权: 有 add 角色才能访问 add 请求filterMap.put("/user/add", "roles[add]");// 授权:必须有 user:update 权限才能访问 updatefilterMap.put("/user/update", "perms[user:update]");// 拦截: 对 user下资源必须登陆后才能访问filterMap.put("/user/**", "authc");// 登出filterMap.put("/logout", "logout");// 对所有用户认证必须登录后访问filterMap.put("/**", "authc");// 将设置了请求权限的 Map 集合放到 过滤器对象中bean.setFilterChainDefinitionMap(filterMap);// 未登录跳转到登录页bean.setLoginUrl("/toLogin");// 登陆成功请求;跳转首页bean.setSuccessUrl("/index");// 认证异常请求处理:权限不足bean.setUnauthorizedUrl("/unauthorized");return bean;}// 配置 ShiroDialect 方言标签整合 Thymeleaf@Beanpublic ShiroDialect shiroDialect(){return new ShiroDialect();}
}
自定义 Shiro 过滤器
AuthorizingRealm
,实现两个抽象方法分别完成授权和认证的逻辑anon
:无需认证即可访问,游客身份authc
:必须认证、登录 才能访问authcBasic
:需要通过 httpBasic 认证user
:不一定已通过认证 rememberMe
perms
:必须拥有对某个资源的访问权限(授权)才能访问role
:必须拥有某个角色权限才能访问port
:请求的端口必须为指定值才可以访问rest
:请求必须是 RESTful ssl
:必须是安全的 URL 请求,协议为 HTTPSAuthenticationException
:认证异常 Shiro
在登录认证过程中,认证失败需要抛出的异常 CredentitalsException
:凭证异常 IncorrectCredentialsException
:不正确的凭证ExpiredCredentialsException
:凭证过期AccountException
:账号异常 ConcurrentAccessException
:并发访问异常,多个用户同时登录时抛出UnknownAccountException
:未知的账号ExcessiveAttemptsException
:认证次数超过限制DisabledAccountException
: 禁用的账号LockedAccountException
:账号被锁定UnsupportedTokenException
:使用了不支持的TokenAuthorizationException
: 授权异常 Shiro
在登录认证过程中,授权失败需要抛出的异常 UnauthorizedException
UnanthenticatedException
Insert title here index
Please login
Welcome back John! Not John? Click here to login.
Hello, , how are you today?
Update your contact informationHello, , how are you today?
Please login in order to update your credit card information.
Administer the systemSorry, you are not allowed to developer the system.
You are a developer and a admin.
You are a admin, vip, or developer.
添加用户Sorry, you are not allowed to delete user accounts.
You can see or add users.
You can see or delete users.
Create a new User
上一篇:质量管理PPAP说明