首先搭建shiro的登录环境
1建立对应的文件login.jsp,login.js
注意点:静态文件必须放在新建的webapp目录的WEB-INF目录下,我之前在这边栽了很多次,当你的静态文件没有放到webapp目录下,而是放到类似于resources(根目录的)目录下,springboot是找不到的,即使你在静态资源映射配置中设了也不行。
2.设置视图解析器
springboot中的视图解析器是在自定义的webMvc配置类中重写addViewResolver方法。
这里我是用了jsp支持的jstl类,设置路径返回的url的前缀跟后缀。(springboot中的提供了在properties中设置前后缀的属性)
3.设置静态资源的映射
同理,springboot静态资源的映射也是在自定义的webMvc配置类中重写addResources***方法,通过设置registry的addResources跟addResourcesLocation方法.addResources()中添加所要映射的url,意味着匹配的url都会转换为location后面的url。
设置完启动springboot,打开login页面,控制台可能会报jstl的config类NotFoundException,我们需要在pom中添加jstl的依赖:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
重新打开login页面。如果这次提示的是下载login这个文件,说明现在没有支持tomcat的返回,
需要在pom中添加浏览器对tomcat的支持依赖:
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>9.0.14</version>
</dependency>
自此,我们重新启动springboot,应该能访问的到login页面。
控制台如果报no mapper ** found ,检查下自己的视图解析器跟静态资源映射有没有写对。
配置类的Configuration注解有没有加,主程序的EnableWebMvc注解有没有。
控制台如果没有报错,输出iframework 初始化完成,那么就检查下你的静态文件有没有放到webapp目录下。
配置shiro
shiro留给我们做的工作其实很简单,shiro已经帮我们在org.apache.shiro下完成了认证,授权的前后过程,我们只要为我们的项目去编写对应的认证,授权条件跟流程。
1.编写自定义的Realm
shiro提供了AuthorizingRealm这个父类,我们只需要编写个AuthorizingRealm的子类去实现我们业务认证授权的代码:
public class MyShiroRealm extends AuthorizingRealm {
@Autowired
private UserReposity userReposity;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
String username = (String) principalCollection.getPrimaryPrincipal();
User user = userReposity.findOneByUsername(username);
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
for(Role role : user.getRoleSet()){
simpleAuthorizationInfo.addRole(role.getRoleName());
}
simpleAuthorizationInfo.addStringPermission(user.getPermission());
return simpleAuthorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
if(authenticationToken.getPrincipal() == null){
return null;
}
String username = (String) authenticationToken.getPrincipal();
User user = userReposity.findOneByUsername(username);
if(user == null){
return null;
}
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username,user.getUserPassword(),getName());
return simpleAuthenticationInfo;
}
}
认证方法doGetAuthorizationInfo:
用户登录的时候我们需要认证当前登录的用户,authenticationToken中保存了用户的信息,getPrincipal()方法可以获取到用户的username,
然后通过我们业务的dao层去获取当前用户名在数据库信息,之后交给simpleAuthenticationInfo 对象去进行校验。如果用户名账号密码匹配,则返回一个正常的simpleAuthenticationInfo 对象,里面会有用户认证成功的信息。如果密码不匹配,则抛出NoAcountExcetion或者密码不正确的异常。
授权方法doGetAuthorizationInfo:
用户认证完成之后会进行授权流程,授权流程是在登陆成功之后才会走。授权会给将用户的角色信息,权限信息都返回给simpleAuthorizationInfo对象。我们可以在此利用simpleAuthorizationInfo对象给用户弹出对应于其角色跟权限的菜单。
进行授权完成之后,我们可以在一些加了权限注解的方法上进行角色,权限的控制
@RequestMapping("create")
@RequiresPermissions({"save"})
@RequiresRoles({"admin"})
public Map<String,Object> save(){
concurrentMap.put("data","success");
return concurrentMap;
}
@RequiresPermissions是对所有未拥有其参数的权限的用户进行拦截,@RequiresRoles则是对非admin用户进行拦截。这个方法就是角色为admin且拥有save权限的用户开放。
自定义的realm完成之后,就需要写一个shiro配置类去配置我们完成的realm
编写shiro配置类
@Configuration
public class ShiroFilter {}
将MyShiroRealm放入到容器中
@Bean
public MyShiroRealm myShiroRealm(){
return new MyShiroRealm();
}
将安全管理器SecurityManager放入到容器中,并且为其配上realm
@Bean
public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
return securityManager;
}
将ShiroFilterFactoryBean 放入到容器中,并且为ShiroFilterFactoryBean 设置安全管理器securityManager。shiroFilterFactoryBean需要注意的 重点:
- setLoginUrl设置起始登录的页面login,该设置成功以后,所有未经过认证的用户对服务器进行访问都只会弹出login这个地址。
- setSuccessUrl设置认证成功之后弹出的地址index.
- setFilterChainDefinitionMaps设置地址过滤的细节
map.put("/",“authc”);意味着所有的url请求都要经过认证;
map.put("/**",“user”);所有url请求在登录之后不做检查;
map.put("/",“anno”);所有url请求支持匿名访问,就是不设检查
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String,String> map = new HashMap<>();
map.put("/**","user");
map.put("/**","authc");
// 设置login页面
shiroFilterFactoryBean.setLoginUrl("/login");
// 设置index主页
shiroFilterFactoryBean.setSuccessUrl("/index");
shiroFilterFactoryBean.setUnauthorizedUrl("/error");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
当发现请求的jsp文件无法找到对应的js文件时,检查这里是不是没有对js等资源文件做匿名访问的支持。
这里AuthorizationAttributeSourceAdvisor 的作用暂时不明。
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
这里贴下我controller的代码
@Controller
public class LoginController {
private final ConcurrentMap<String,Object> concurrentMap = new ConcurrentHashMap<>();
@RequestMapping(value = "/login",method = RequestMethod.GET)
public String login(){
return new "login";
}
@RequestMapping(value = "/login",method = RequestMethod.POST)
public String login(User user){
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(user.getUserName(),user.getUserPassword());
subject.login(usernamePasswordToken);
return "redirect:/index";
}
@RequestMapping("/error")
public String error(){
return "error";
}
@RequestMapping("/index")
public String index(){
return "index";
}
@RequestMapping("create")
@RequiresPermissions({"save"})
@RequiresRoles({"admin"})
public Map<String,Object> save(){
concurrentMap.put("data","success");
return concurrentMap;
}
}